package hos.java.util;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;

import hos.java.exception.CloneFailedException;
import hos.java.func.FunctionReturn;

/**
 * <p>Title: AnyUtils </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @version : 1.0
 * @date : 2022/7/2 16:20
 */
public class AnyUtils {

    //<editor-fold desc="空、对比、">

    public int count(final Object data) {
        if (data == null) {
            return 0;
        }
        if (data instanceof CharSequence && (((CharSequence) data).length() == 0 || "null".equalsIgnoreCase((String) data)
                || "NULL".equalsIgnoreCase((String) data) || "Null".equalsIgnoreCase((String) data))) {
            return 0;
        }
        if (data instanceof CharSequence) {
            return ((CharSequence) data).length();
        }
        if (data instanceof Collection) {
            return ((Collection<?>) data).size();
        }
        if (data instanceof Map) {
            return ((Map<?, ?>) data).size();
        }
        int count;
        if (data instanceof Iterator) {
            final Iterator<?> iter = (Iterator<?>) data;
            count = 0;
            while (iter.hasNext()) {
                count++;
                iter.next();
            }
            return count;
        }
        if (data instanceof Enumeration) {
            final Enumeration<?> enumeration = (Enumeration<?>) data;
            count = 0;
            while (enumeration.hasMoreElements()) {
                count++;
                enumeration.nextElement();
            }
            return count;
        }
        return 1;
    }

    /**
     * 对象中是否包含元素<br>
     * 支持的对象类型包括：
     * <ul>
     * <li>String</li>
     * <li>Collection</li>
     * <li>Map</li>
     * <li>Iterator</li>
     * <li>Enumeration</li>
     * <li>Array</li>
     * </ul>
     *
     * @param obj     对象
     * @param element 元素
     * @return 是否包含
     */
    public static boolean contains(Object obj, Object element) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof String) {
            if (element == null) {
                return false;
            }
            return ((String) obj).contains(element.toString());
        }
        if (obj instanceof Collection) {
            return ((Collection<?>) obj).contains(element);
        }
        if (obj instanceof Map) {
            return ((Map<?, ?>) obj).containsValue(element);
        }

        if (obj instanceof Iterator) {
            final Iterator<?> iter = (Iterator<?>) obj;
            while (iter.hasNext()) {
                final Object o = iter.next();
                if (equals(o, element)) {
                    return true;
                }
            }
            return false;
        }
        if (obj instanceof Enumeration) {
            final Enumeration<?> enumeration = (Enumeration<?>) obj;
            while (enumeration.hasMoreElements()) {
                final Object o = enumeration.nextElement();
                if (equals(o, element)) {
                    return true;
                }
            }
            return false;
        }
        if (obj.getClass().isArray() == true) {
            final int len = Array.getLength(obj);
            for (int i = 0; i < len; i++) {
                final Object o = Array.get(obj, i);
                if (equals(o, element)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Return whether object is empty.
     *
     * @param obj The object.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean isEmpty(final Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof CharSequence && ((CharSequence) obj).length() == 0) {
            return true;
        }
        if (obj instanceof CharSequence) {
            String targetCase = obj.toString();
            return targetCase.isEmpty() || targetCase.equals("null") || targetCase.equals("Null") || targetCase.equals("NULL");
        }
        if (obj.getClass().isArray() && Array.getLength(obj) == 0) {
            return true;
        }
        if (obj instanceof Collection && ((Collection) obj).isEmpty()) {
            return true;
        }
        if (obj instanceof Map && ((Map) obj).isEmpty()) {
            return true;
        }
        return false;
    }

    /**
     * Return whether object is not empty.
     *
     * @param obj The object.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean isNotEmpty(final Object obj) {
        return !isEmpty(obj);
    }

    /**
     * Return whether object1 is equals to object2.
     *
     * @param o1 The first object.
     * @param o2 The second object.
     * @return {@code true}: yes<br>{@code false}: no
     */
    @SuppressWarnings("EqualsReplaceableByObjectsCall")
    public static boolean equals(final Object o1, final Object o2) {
        if (o1 == o2) {
            return true;
        }
        if (o1 instanceof CharSequence && o2 instanceof CharSequence) {
            CharSequence a = (CharSequence) o1;
            CharSequence b = (CharSequence) o2;
            int length;
            if ((length = a.length()) == b.length()) {
                if (a instanceof String && b instanceof String) {
                    return a.equals(b);
                } else {
                    for (int i = 0; i < length; i++) {
                        if (a.charAt(i) != b.charAt(i)) return false;
                    }
                    return true;
                }
            }
        }
        return o1 != null && o1.equals(o2);
    }

    public static boolean notEqual(Object object1, Object object2) {
        return !equals(object1, object2);
    }

    /**
     * Returns 0 if the arguments are identical and {@code
     * c.compare(a, b)} otherwise.
     * Consequently, if both arguments are {@code null} 0
     * is returned.
     */
    public static <T> int compare(T a, T b, Comparator<? super T> c) {
        return (a == b) ? 0 : c.compare(a, b);
    }


    /**
     * Return the hash code of object.
     *
     * @param o The object.
     * @return the hash code of object
     */
    public static int hashCode(final Object o) {
        return o != null ? o.hashCode() : 0;
    }

    /**
     * Return the hash code of objects.
     */
    public static int hashCodes(Object... values) {
        return Arrays.hashCode(values);
    }
    //</editor-fold>

    //<editor-fold desc="异常">

    /**
     * Checks that the specified object reference is not {@code null}.
     */
    public static <T> T requireNonNull(T obj) {
        if (obj == null) throw new NullPointerException();
        return obj;
    }

    /**
     * Checks that the specified object reference is not {@code null} and
     * throws a customized {@link NullPointerException} if it is.
     */
    public static <T> T requireNonNull(T obj, String ifNullTip) {
        if (obj == null) throw new NullPointerException(ifNullTip);
        return obj;
    }

    /**
     * Require the objects are not null.
     *
     * @param objects The object.
     * @throws NullPointerException if any object is null in objects
     */
    public static void requireNonNulls(final Object... objects) {
        if (objects == null) throw new NullPointerException();
        for (Object object : objects) {
            if (object == null) throw new NullPointerException();
        }
    }
    //</editor-fold>


    //<editor-fold desc="T">

    /**
     * Return the nonnull object or default object.
     *
     * @param object        The object.
     * @param defaultObject The default object to use with the object is null.
     * @param <T>           The value type.
     * @return the nonnull object or default object
     */
    public static <T> T getIfNull(final T object, final T defaultObject) {
        if (isEmpty(object)) {
            return defaultObject;
        }
        return object;
    }

    public static <T> T getIfNull(T object, FunctionReturn<T> defaultSupplier) {
        if (isEmpty(object)) {
            if (defaultSupplier == null) {
                return null;
            }
            return defaultSupplier.invoke();
        }
        return object;
    }


    public static <T> T toNull(T obj) {
        return getIfNull(obj, null);
    }
    //</editor-fold>


    //<editor-fold desc="String">

    public static String toNullString(Object value) {
        Object target = toNull(value);
        if (target == null) {
            return null;
        }
        return String.valueOf(target);
    }

    public static String toString(Object obj, FunctionReturn<String> supplier) {
        return obj == null ? (supplier == null ? null : (String) supplier.invoke()) : obj.toString();
    }

    public static String toString(Object obj, String defaultString) {
        return obj == null ? (defaultString) : obj.toString();
    }

    public static String empty(Object target, String start, String end, String defaultValue) {
        target = toNullString(target);
        if (target == null) {
            return start + defaultValue + end;
        }
        return start + target + end;
    }

    public static String empty(Object target, String start, String end, FunctionReturn<String> supplier) {
        target = toNullString(target);
        if (target == null) {
            if (supplier == null) {
                return start + end;
            }
            return start + supplier.invoke() + end;
        }
        return start + target + end;
    }

    public static String empty(Object value, String defaultValue) {
        return empty(value, "", "", defaultValue);
    }

    public static String empty(Object value, FunctionReturn<String> supplier) {
        return empty(value, "", "", supplier);
    }

    public static String empty(Object value) {
        return empty(value, "");
    }

    public static String divide(Object value) {
        return empty(value, "-");
    }

    public static String toEnd(Object value, String end) {
        return empty(value, "", end, "");
    }

    public static String toStart(Object value, String start) {
        return empty(value, start, "", "");
    }

    public static String rate(Object value, String defaultNull) {
        return empty(value, "", "%", defaultNull);
    }

    public static String rate(Object value) {
        return rate(value, "");
    }

    public static String like(Object target) {
        String like = toNullString(target);
        if (like == null || like.isEmpty()) {
            return "%%";
        }
        return "%" + like + "%";
    }
    //</editor-fold>

    //<editor-fold desc="Number">
    public static Integer asInteger(Object value, Integer defaultValue) {
        Object target = toNull(value);
        if (target == null) {
            return defaultValue;
        }
        try {
            if (target instanceof Integer) {
                return (Integer) target;
            } else if (target instanceof Number) {
                return ((Number) target).intValue();
            } else if (target instanceof Boolean) {
                if (asBoolean(target)) {
                    return 1;
                }
                return 0;
            } else if (target instanceof CharSequence) {
                String targetStr = empty(target);
                if (targetStr.equals("true")) {
                    return 1;
                }
                if (targetStr.equals("false")) {
                    return 0;
                }
                if ("是".equalsIgnoreCase(targetStr)) {
                    return 1;
                } else if ("否".equalsIgnoreCase(targetStr)) {
                    return 0;
                } else if ("有".equalsIgnoreCase(targetStr)) {
                    return 1;
                } else if ("无".equalsIgnoreCase(targetStr)) {
                    return 0;
                } else if ("1".equalsIgnoreCase(targetStr)) {
                    return 1;
                } else if ("0".equalsIgnoreCase(targetStr)) {
                    return 0;
                }
                try {
                    return (int) Integer.parseInt(empty(target));
                } catch (NumberFormatException ignored) {
                }
            }
        } catch (Exception ignored) {
        }
        return defaultValue;
    }

    public static Integer toNullInteger(Object value) {
        return asInteger(value, null);
    }

    public static int asInteger(Object value) {
        return asInteger(value, 0);
    }

    public static String toNullIntegerString(Object value) {
        return toNullString(toNullInteger(value));
    }

    public static String asIntegerString(Object value, Integer defaultValue) {
        Object target = toNullInteger(value);
        if (target == null) {
            return String.valueOf(defaultValue);
        }
        return empty(target);
    }

    public static String asIntegerString(Object value) {
        return asIntegerString(value, 0);
    }

    public static Boolean asBoolean(Object value, Boolean defaultValue) {
        Object target = toNull(value);
        if (target == null) {
            return defaultValue;
        }
        try {
            if (target instanceof Boolean) {
                return (Boolean) target;
            } else if (target instanceof CharSequence) {
                String stringValue = (String) target;
                if ("true".equalsIgnoreCase(stringValue)) {
                    return true;
                } else if ("false".equalsIgnoreCase(stringValue)) {
                    return false;
                } else if ("是".equalsIgnoreCase(stringValue)) {
                    return true;
                } else if ("否".equalsIgnoreCase(stringValue)) {
                    return false;
                } else if ("有".equalsIgnoreCase(stringValue)) {
                    return true;
                } else if ("无".equalsIgnoreCase(stringValue)) {
                    return false;
                } else if ("1".equalsIgnoreCase(stringValue)) {
                    return true;
                } else if ("0".equalsIgnoreCase(stringValue)) {
                    return false;
                }
            } else if (target instanceof Integer) {
                if ((int) target == 0) {
                    return false;
                } else if ((int) target == 1) {
                    return true;
                }
            } else if (target instanceof Float) {
                if ((float) target == 0f) {
                    return false;
                } else if ((float) target == 1f) {
                    return true;
                }
            } else if (target instanceof Long) {
                if ((long) target == 0L) {
                    return false;
                } else if ((long) target == 1L) {
                    return true;
                }
            } else if (target instanceof Double) {
                if ((double) target == 0D) {
                    return false;
                } else if ((double) target == 1D) {
                    return true;
                }
            }
        } catch (Exception ignored) {
        }
        return defaultValue;
    }

    public static boolean asBoolean(Object value) {
        return asBoolean(value, false);
    }

    public static Boolean toNullBoolean(Object value) {
        return asBoolean(value, null);
    }

    public static String toNullBooleanString(Object value) {
        return toNullString(toNullBoolean(value));
    }

    public static String asBooleanString(Object value, Boolean defaultValue) {
        Object target = toNullBoolean(value);
        if (target == null) {
            return String.valueOf(defaultValue);
        }
        return empty(target);
    }

    public static String asBooleanString(Object value) {
        return asBooleanString(value, false);
    }

    public static Double asDouble(Object value, Double defaultValue) {
        Object target = toNull(value);
        if (target == null) {
            return defaultValue;
        }
        try {
            if (target instanceof Double) {
                return (Double) target;
            } else if (target instanceof Number) {
                return ((Number) target).doubleValue();
            } else if (target instanceof Boolean) {
                if (asBoolean(target)) {
                    return 1D;
                }
                return 0D;
            } else if (target instanceof CharSequence) {
                String targetStr = empty(target);
                if (targetStr.equalsIgnoreCase("true")) {
                    return 1D;
                }
                if (targetStr.equalsIgnoreCase("false")) {
                    return 0D;
                }
                if ("是".equalsIgnoreCase(targetStr)) {
                    return 1D;
                } else if ("否".equalsIgnoreCase(targetStr)) {
                    return 0D;
                } else if ("1".equalsIgnoreCase(targetStr)) {
                    return 1D;
                } else if ("0".equalsIgnoreCase(targetStr)) {
                    return 0D;
                }
                try {
                    return Double.parseDouble(empty(target));
                } catch (NumberFormatException ignored) {
                }
            }
        } catch (Exception ignored) {
        }
        return defaultValue;
    }

    public static double asDouble(Object value) {
        return asDouble(value, 0.0);
    }

    public static Double toNullDouble(Object value) {
        return asDouble(value, null);
    }

    public static String toNullDoubleString(Object value) {
        return toNullString(toNullDouble(value));
    }

    public static String asDoubleString(Object value, Double defaultValue) {
        Object target = toNullDouble(value);
        if (target == null) {
            return String.valueOf(defaultValue);
        }
        return empty(target);
    }

    public static String asDoubleString(Object value) {
        return asDoubleString(value, 0.0);
    }

    public static Long asLong(Object value, Long defaultValue) {
        Object target = toNull(value);
        if (target == null) {
            return defaultValue;
        }
        try {
            if (target instanceof Long) {
                return (Long) target;
            } else if (target instanceof Number) {
                return ((Number) target).longValue();
            } else if (target instanceof Boolean) {
                if (asBoolean(target)) {
                    return 1L;
                }
                return 0L;
            } else if (target instanceof CharSequence) {
                String targetStr = empty(target);
                if (targetStr.equalsIgnoreCase("true")) {
                    return 1L;
                }
                if (targetStr.equalsIgnoreCase("false")) {
                    return 0L;
                }
                if ("是".equalsIgnoreCase(targetStr)) {
                    return 1L;
                } else if ("否".equalsIgnoreCase(targetStr)) {
                    return 0L;
                } else if ("1".equalsIgnoreCase(targetStr)) {
                    return 1L;
                } else if ("0".equalsIgnoreCase(targetStr)) {
                    return 0L;
                }
                try {
                    return (long) Long.parseLong((String) target);
                } catch (NumberFormatException ignored) {
                }
            }
        } catch (Exception ignored) {
        }
        return defaultValue;
    }

    public static long asLong(Object value) {
        return asLong(value, 0L);
    }

    public static Long toNullLong(Object value) {
        return asLong(value, null);
    }

    public static String toNullLongString(Object value) {
        return toNullString(toNullLong(value));
    }

    public static String asLongString(Object value, Long defaultValue) {
        Object target = toNullLong(value);
        if (target == null) {
            return String.valueOf(defaultValue);
        }
        return empty(target);
    }

    public static String asLongString(Object value) {
        return asLongString(value, 0L);
    }

    public static Float asFloat(Object value, Float defaultValue) {
        Object target = toNull(value);
        if (target == null) {
            return defaultValue;
        }
        try {
            if (target instanceof Float) {
                return (Float) target;
            } else if (target instanceof Number) {
                return ((Number) target).floatValue();
            } else if (target instanceof Boolean) {
                if (asBoolean(target)) {
                    return 1f;
                }
                return 0f;
            } else if (target instanceof CharSequence) {
                String targetStr = empty(target);
                if (targetStr.equals("true")) {
                    return 1f;
                }
                if (targetStr.equals("false")) {
                    return 0f;
                }
                if ("是".equalsIgnoreCase(targetStr)) {
                    return 1F;
                } else if ("否".equalsIgnoreCase(targetStr)) {
                    return 0F;
                } else if ("1".equalsIgnoreCase(targetStr)) {
                    return 1F;
                } else if ("0".equalsIgnoreCase(targetStr)) {
                    return 0F;
                }
                try {
                    return Float.parseFloat((String) target);
                } catch (NumberFormatException ignored) {
                }
            }
        } catch (Exception ignored) {
        }
        return defaultValue;
    }

    public static float asFloat(Object value) {
        return asFloat(value, 0f);
    }

    public static Float toNullFloat(Object value) {
        return asFloat(value, null);
    }

    public static String toNullFloatString(Object value) {
        return toNullString(toNullFloat(value));
    }

    public static String asFloatString(Object value, Float defaultValue) {
        Object target = toNullFloat(value);
        if (target == null) {
            return String.valueOf(defaultValue);
        }
        return empty(target);
    }

    public static String asFloatString(Object value) {
        return asFloatString(value, 0f);
    }
    //</editor-fold>


    public static String isYesOrNo(Object target, boolean defaultValue, String yes, String no) {
        if (asBoolean(target, defaultValue))
            return yes;
        else return no;
    }

    public static String isYesOrNo(Object target, String yes, String no) {
        if (asBoolean(target, false))
            return yes;
        else return no;
    }

    public static String isYesOrNo(Object target, boolean defaultValue) {
        return isYesOrNo(target, defaultValue, "是", "否");
    }

    public static String isYesOrNo(Object target) {
        return isYesOrNo(target, "是", "否");
    }

    public static <T> T clone(T obj) {
        if (!(obj instanceof Cloneable)) {
            return null;
        } else {
            Object result;
            if (obj.getClass().isArray()) {
                Class<?> componentType = obj.getClass().getComponentType();
                if (componentType.isPrimitive()) {
                    int length = Array.getLength(obj);
                    result = Array.newInstance(componentType, length);

                    while (length-- > 0) {
                        Array.set(result, length, Array.get(obj, length));
                    }
                } else {
                    result = ((Object[]) ((Object[]) obj)).clone();
                }
            } else {
                try {
                    Method clone = obj.getClass().getMethod("clone");
                    result = clone.invoke(obj);
                } catch (NoSuchMethodException var4) {
                    throw new CloneFailedException("Cloneable type " + obj.getClass().getName() + " has no clone method", var4);
                } catch (IllegalAccessException var5) {
                    throw new CloneFailedException("Cannot clone Cloneable type " + obj.getClass().getName(), var5);
                } catch (InvocationTargetException var6) {
                    throw new CloneFailedException("Exception cloning Cloneable type " + obj.getClass().getName(), var6.getCause());
                }
            }
            return (T) result;
        }
    }
}
